home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Add-ons / VideoShop Plug-In Kit 1.0 / SampleFilt.p < prev    next >
Text File  |  1992-05-06  |  15KB  |  582 lines

  1. {
  2.  
  3.     SampleFilt.p
  4.     
  5.     Sample Filter Effect for DiVA VideoShop
  6.     
  7.     04/28/92    Iván Cavero Belaúnde
  8.     
  9.  
  10. }
  11.  
  12. UNIT    SampleFilter;
  13.  
  14. INTERFACE
  15.  
  16. USES
  17.     Types,Memory,Resources,QuickDraw,QDOffscreen,Dialogs,OSUtils,Packages,Errors,
  18.     ToolUtils,SysEqu,GestaltEqu,DiVAFilter;
  19.  
  20. FUNCTION FilterEntryPoint(selector: INTEGER; params: FiltParamPtr; VAR data: LONGINT;
  21.     currentStep, totalSteps: INTEGER): FiltErr;
  22.  
  23. FUNCTION DoParameters(params: FiltParamPtr): FiltErr;
  24.  
  25. FUNCTION DoProcessFrame(params: FiltParamPtr; currentStep, totalSteps: INTEGER):
  26.     FiltErr;
  27.  
  28. PROCEDURE DoFilter(src: PixMapHandle; destGW: GWorldPtr; current,total,intensity:
  29.     INTEGER);
  30.  
  31. IMPLEMENTATION
  32.  
  33. CONST
  34.  
  35. {    Shades of gray for 3D objects    }
  36.     kLightGray    =    $ffff;
  37.     kMediumGray    =    $dddd;
  38.     kDarkGray    =    $aaaa;
  39.  
  40. {    Dialog item constants    }
  41.  
  42.     dialogID            =    16001;
  43.     defaultItem            =    3;
  44.     intensityItem        =    5;
  45.     dividerLinesItem    =    7;
  46.     imageItem            =    9;
  47.     frameItem            =    10;
  48.     forwardItem            =    11;
  49.     backwardItem        =    12;
  50.  
  51. TYPE
  52.     
  53.     ParamsRecord = RECORD
  54.         filterIntensity:    INTEGER;
  55.         backward:            BOOLEAN;
  56.         filterParamBlock:    FiltParamPtr;    {    Paramblk for use of custom ditems    }
  57.     END;
  58.     
  59.     ParamsPtr        =    ^ParamsRecord;
  60.     ParamsHandle    =    ^ParamsPtr;
  61.     
  62.     RGBColorPtr    =    ^RGBColor;
  63.  
  64. FUNCTION FilterEntryPoint(selector: INTEGER; params: FiltParamPtr; VAR data: LONGINT;
  65.     currentStep, totalSteps: INTEGER): FiltErr;
  66. BEGIN
  67.     CASE selector OF
  68.         FiltParameters:
  69.             FilterEntryPoint := DoParameters (params);
  70.         FiltPrepare:
  71.             FilterEntryPoint := noErr;
  72.         FiltStart:
  73.             FilterEntryPoint := noErr;
  74.         FiltProcessFrame:
  75.             FilterEntryPoint := DoProcessFrame(params,currentStep,totalSteps);
  76.         FiltFinish:
  77.             FilterEntryPoint := noErr;
  78.         OTHERWISE
  79.             FilterEntryPoint := -1;
  80.     END;
  81. END;
  82.  
  83. FUNCTION DoTestAbort(codeAddress: ProcPtr): BOOLEAN;
  84. {    Inline code to call the TestAbort function    }
  85.     INLINE    $205F,        {    move.l    (a7)+,a0    pop procPtr from stack    }
  86.             $4E90;        {    jsr        (a0)        and call it                }
  87.  
  88. FUNCTION TestAbort (params: FiltParamPtr): BOOLEAN;
  89. BEGIN
  90.     TestAbort := DoTestAbort(params^.abortProc);
  91. END;
  92.  
  93. PROCEDURE DoUpdateProgress(done, total: LONGINT; codeAddress: ProcPtr);
  94. {    Inline code to call the UpdateProgress function    }
  95.     INLINE    $205F,        {    move.l    (a7)+,a0    pop procPtr from stack    }
  96.             $4E90;        {    jsr        (a0)        and call it                }
  97.  
  98. PROCEDURE UpdateProgress(params: FiltParamPtr; done, total: LONGINT);
  99. BEGIN
  100.     DoUpdateProgress(done,total,params^.progressProc);
  101. END;
  102.  
  103. PROCEDURE CenterDialogBestDevice(dt: DialogTHndl);
  104. {
  105.     Given a dialog template in dt, modify it so as to center the dialog on the
  106.     best (deepest) device.    Used to place options dialog (with image preview)
  107.     in that device.
  108. }
  109. VAR
  110.     width, height:    INTEGER;
  111.     maxDevice:        GDHandle;
  112.     grayRgn:        RgnHandle;
  113.     r,dr:            Rect;
  114. BEGIN
  115.     grayRgn := GetGrayRgn;
  116.     r := grayRgn^^.rgnBBox;
  117.     
  118.     maxDevice := GetMaxDevice(r);
  119.     r := maxDevice^^.gdRect;
  120.     width := r.right - r.left;
  121.     height := r.bottom - r.top;
  122.     
  123.     dr := dt^^.boundsRect;
  124.     OffsetRect (dr, r.left-dr.left, r.top-dr.top);
  125.     OffsetRect (dr, (width - (dr.right-dr.left)) DIV 2,
  126.                     (height - (dr.bottom-dr.top) - GetMBarHeight) DIV 3 + GetMBarHeight);
  127.     dt^^.boundsRect := dr;
  128. END;
  129.  
  130. FUNCTION GetWindowGDevice(w: WindowPtr): GDHandle;
  131. {
  132.     Find the deepest gdevice the window intersects. Used for determining the depth
  133.     of the screen we're drawing on (to look good on B/W displays).
  134. }
  135. VAR
  136.     r:            Rect;
  137.     oldPort:    GrafPtr;
  138. BEGIN
  139.     GetPort(oldPort);
  140.     SetPort(w);
  141.     r := w^.portRect;
  142.     LocalToGlobal(r.topLeft);
  143.     LocalToGlobal(r.botRight);
  144.     GetWindowGDevice := GetMaxDevice(r);
  145.     SetPort (oldPort);
  146. END;
  147.  
  148. PROCEDURE DrawLines(theDialog: DialogPtr; itemNo: INTEGER);
  149. {
  150.     userItem drawing routine for dividing lines (with shadow)
  151. }
  152. VAR
  153.     oldColor, greyColor:    RGBColor;
  154.     itemType:    INTEGER;
  155.     me:            Handle;
  156.     box:        Rect;
  157. BEGIN
  158.     GetDItem(theDialog,itemNo,itemType,me,box);
  159.     GetForeColor(oldColor);
  160.     RGBForeColor(RGBColorPtr(RGBBlack)^);
  161.     MoveTo(box.left,box.top);
  162.     LineTo(box.right-2,box.top);
  163.     MoveTo(box.left,box.top+3);
  164.     LineTo(box.right-2,box.top+3);
  165.     IF (GetWindowGDevice(theDialog)^^.gdPMap^^.pixelSize >= 8) THEN
  166.         BEGIN
  167.             greyColor.red := $7777;
  168.             greyColor.green := $7777;
  169.             greyColor.blue := $7777;
  170.             RGBForeColor (greyColor);
  171.             MoveTo(box.left+1,    box.top+1);
  172.             LineTo(box.right-1,    box.top+1);
  173.             MoveTo(box.left+1,    box.top+4);
  174.             LineTo(box.right-1,    box.top+4);
  175.         END;
  176.     RGBForeColor(oldColor);
  177. END;
  178.  
  179.  
  180. PROCEDURE OutlineOK(dp: DialogPtr; item: INTEGER);
  181. {
  182.     userItem drawing routine for outlining the default button in the dialog box
  183. }
  184. VAR
  185.     r:    Rect;
  186.     h:    Handle;
  187.     itemType:    INTEGER;
  188. BEGIN
  189.     item := ok;
  190.     GetDItem (dp, item, itemType, h, r);
  191.     PenNormal;
  192.     PenSize (3, 3);
  193.     InsetRect (r, -4, -4);
  194.     FrameRoundRect (r, 16, 16);
  195.     PenNormal;
  196. END;
  197.  
  198.  
  199. PROCEDURE DrawSunkFrame(dp: DialogPtr; item: INTEGER);
  200. {
  201.     User item drawing routine for drawing the 3-D-like frame for the preview image.
  202. }
  203. VAR
  204.     r:    Rect;
  205.     h:    Handle;
  206.     itemType:    INTEGER;
  207.     oldForeColor, darkGray, lightGray:        RGBColor;
  208.     bwMode:    Boolean;
  209. BEGIN
  210.     bwMode := (GetWindowGDevice(dp)^^.gdPMap^^.pixelSize < 8);
  211.     GetDItem (dp, item, itemType, h, r);
  212.     darkGray.red    :=    kDarkGray;
  213.     darkGray.green    :=    kDarkGray;
  214.     darkGray.blue    :=    kDarkGray;
  215.     lightGray.red    :=    kLightGray;
  216.     lightGray.green    :=    kLightGray;
  217.     lightGray.blue    :=    kLightGray;
  218.     GetForeColor(oldForeColor);
  219.     if (NOT bwMode) THEN
  220.         BEGIN
  221.             RGBForeColor(darkGray);
  222.             MoveTo(r.left,r.bottom-1);
  223.             LineTo(r.left,r.top);
  224.             LineTo(r.right-1,r.top);
  225.         END
  226.     ELSE RGBForeColor(RGBColorPtr(RGBBlack)^);
  227.     MoveTo(r.left+1,r.bottom-2);
  228.     LineTo(r.left+1,r.top+1);
  229.     LineTo(r.right-2,r.top+1);
  230.     IF (NOT bwMode) THEN
  231.         BEGIN
  232.             RGBForeColor(lightGray);
  233.             MoveTo(r.right-1,r.top+1);
  234.             LineTo(r.right-1,r.bottom-1);
  235.             LineTo(r.left,r.bottom-1);
  236.         END
  237.     ELSE RGBForeColor(RGBColorPtr(RGBBlack)^);
  238.     MoveTo(r.right-2,r.top+2);
  239.     LineTo(r.right-2,r.bottom-2);
  240.     LineTo(r.left+1,r.bottom-2);
  241.     InsetRect(r,2,2);
  242.     RGBForeColor(RGBColorPtr(RGBBlack)^);
  243.     FrameRect(r);
  244.     RGBForeColor(oldForeColor);
  245. END;
  246.  
  247. PROCEDURE RectLocalToGlobal (VAR r:Rect);
  248. BEGIN
  249.     LocalToGlobal(r.topLeft);
  250.     LocalToGlobal(r.botRight);
  251. END;
  252.  
  253. PROCEDURE DrawFilteredImage (dp: DialogPtr; item: INTEGER);
  254. {
  255.     User item drawing routine to draw the preview image
  256. }
  257. VAR
  258.     r:                Rect;
  259.     h:                Handle;
  260.     itemType:        INTEGER;
  261.     oldGD, destGD:    GDHandle;
  262.     theParams:        ParamsHandle;
  263.     params:            FiltParamPtr;
  264.     oldState:        SignedByte;
  265.     filtered:        PixMapHandle;
  266.     oldFore,oldBack:    RGBColor;
  267.  
  268. BEGIN
  269.     destGD := GetWindowGDevice(dp);
  270.     theParams := ParamsHandle(GetWRefCon(dp));
  271.     params := theParams^^.filterParamBlock;
  272.     
  273.     IF (IntegerPtr(SysVersion)^<$700) THEN
  274.         filtered := params^.destImage^.portPixMap
  275.     ELSE
  276.         filtered := GetGWorldPixMap (params^.destImage);
  277.     
  278.     GetDItem (dp, item, itemType, h, r);
  279.     oldGD := GetGDevice;
  280.     SetGDevice (destGD);
  281.     GetForeColor (oldFore);
  282.     GetBackColor (oldBack);
  283.     RGBForeColor (RGBColorPtr(RGBBlack)^);
  284.     RGBBackColor (RGBColorPtr(RGBWhite)^);
  285.     oldState := HGetState (Handle(CGrafPtr(dp)^.portPixMap));
  286.     HLock (Handle(CGrafPtr(dp)^.portPixMap));
  287.     CopyBits(BitMapHandle(filtered)^^,dp^.portBits,filtered^^.bounds,r,srcCopy,nil);
  288.     HSetState (Handle(CGrafPtr(dp)^.portPixMap),oldState);
  289.     RGBForeColor (oldFore);
  290.     RGBBackColor (oldBack);
  291.     SetGDevice (oldGD);
  292. END;
  293.  
  294. PROCEDURE UpdateImage (ctrl: ControlHandle; VAR value: INTEGER);
  295. {
  296.     Callback routine for the control to update the preview image. Gets called by
  297.     VideoShop's slider control definition while the control is being dragged.
  298. }
  299. VAR
  300.     dp:            DialogPtr;
  301.     theParams:    ParamsHandle;
  302.     params:        FiltParamPtr;
  303.     src:        PixMapHandle;
  304.     oldPort:    GrafPtr;
  305. BEGIN    
  306.     dp := ctrl^^.contrlOwner;
  307.     theParams := ParamsHandle(GetWRefCon(dp));
  308.         {    Get the parameters handle from the window's refcon    }
  309.     params := theParams^^.filterParamBlock;
  310.         {    Get the parameter block we stuffed there    }
  311.     src := params^.srcImage;
  312.     theParams^^.filterIntensity := value;
  313.     DoFilter (src, params^.destImage, 100, 100, theParams^^.filterIntensity);
  314.     GetPort (oldPort);
  315.     SetPort (dp);
  316.     DrawFilteredImage (dp,imageItem);
  317.     SetPort (oldPort);
  318. END;
  319.  
  320. PROCEDURE SetupDItems (dp: DialogPtr);
  321. VAR
  322.     itemType:    INTEGER;
  323.     h:            Handle;
  324.     r:            Rect;
  325. BEGIN
  326.     {    Point default useritem to default button outline routine    }
  327.     GetDItem (dp, defaultItem, itemType, h                  , r);
  328.     SetDItem (dp, defaultItem, itemType, Handle(@OutlineOK), r);
  329.     
  330.     {    Point divider lines useritem to dividing lines drawing routine    }
  331.     GetDItem (dp, dividerLinesItem, itemType, h                  , r);
  332.     SetDItem (dp, dividerLinesItem,  itemType, Handle(@DrawLines), r);    
  333.     
  334.     {    Point preview image item to image blitting routine    }
  335.     GetDItem (dp, imageItem, itemType, h                  , r);
  336.     SetDItem (dp, imageItem,  itemType, Handle(@DrawFilteredImage), r);
  337.     
  338.     {    Point preview image frame item to drawing routine    }
  339.     GetDItem (dp, frameItem, itemType, h                  , r);
  340.     SetDItem (dp, frameItem,  itemType, Handle(@DrawSunkFrame), r);
  341.     
  342.     {    Point control's refCon to the the dragging callback routine    }
  343.     GetDItem (dp, intensityItem, itemType, h, r);
  344.     SetCRefCon(ControlHandle(h),LONGINT(@UpdateImage));
  345. END;
  346.  
  347. PROCEDURE RecalcDItems (p: ParamsHandle; dp: DialogPtr);
  348. VAR
  349.     itemType:    INTEGER;
  350.     fwd, back:    ControlHandle;
  351.     r:            Rect;
  352. BEGIN
  353.     GetDItem (dp, intensityItem, itemType, Handle(fwd), r);
  354.     p^^.filterIntensity := GetCtlValue(fwd);
  355.     
  356.     GetDItem (dp, forwardItem, itemType, Handle(fwd), r);
  357.     GetDItem (dp, backwardItem, itemType, Handle(back), r);
  358.     IF (p^^.backward) THEN BEGIN
  359.         SetCtlValue(fwd,0);
  360.         SetCtlValue(back,1);
  361.     END
  362.     ELSE BEGIN
  363.         SetCtlValue(fwd,1);
  364.         SetCtlValue(back,0);
  365.     END;
  366. END;
  367.  
  368. FUNCTION DoParameters (params: FiltParamPtr): FiltErr;
  369. {
  370.     Prompt the user for parameters if necessary.
  371. }
  372. VAR
  373.     dp:                        DialogPtr;
  374.     dt:                        DialogTHndl;
  375.     theParams:                ParamsHandle;
  376.     item:                    INTEGER;
  377.     oldPort:                GrafPtr;
  378.     pmStateSrc,pmStateDest:    GWorldFlags;
  379.     stateSrc,stateDest:        SignedByte;
  380.     src,dest:                PixMapHandle;
  381.     dummy:                    BOOLEAN;
  382.  
  383. BEGIN    
  384.     {    If we didn't get passed a params handle, allocate and initialize it    }
  385.     IF (params^.parameters = NIL) THEN BEGIN
  386.         theParams := ParamsHandle(NewHandleClear(sizeof(ParamsRecord)));
  387.         IF (theParams = NIL) THEN BEGIN
  388.             DoParameters := FiltErrOutOfMemory + MemError;
  389.             Exit(DoParameters);
  390.         END;
  391.         theParams^^.filterIntensity := 80;
  392.     END
  393.     ELSE BEGIN
  394.     {
  395.         Otherwise, we just return. If we required specific hardware or mounds of
  396.         memory we would check here.
  397.     }
  398.         DoParameters := noErr;
  399.         Exit(DoParameters);
  400.     END;
  401.     
  402.     src := params^.srcImage;
  403.     
  404.     {    Prepare for previewing filter    }
  405.     
  406.     {    Get the image pixmaps, dealing with pre-System 7 GetGWorldPixMap bug    }
  407.     IF (IntegerPtr(SysVersion)^ < $700) THEN
  408.         dest := params^.destImage^.portPixMap
  409.     ELSE
  410.         dest := GetGWorldPixMap (params^.destImage);
  411.     
  412.     {    Save pixmaps' state and lock the buffers    }
  413.     pmStateSrc := GetPixelsState(src);
  414.     dummy := LockPixels (src);
  415.     stateSrc := HGetState (Handle(src));
  416.     MoveHHi (Handle(src));
  417.     HLock (Handle(src));
  418.     pmStateDest := GetPixelsState(dest);
  419.     dummy := LockPixels (dest);
  420.     stateDest := HGetState (Handle(dest));
  421.     MoveHHi (Handle(dest));
  422.     HLock (Handle(dest));
  423.     
  424.     theParams^^.filterParamBlock := params;
  425.     
  426.     {    Load in the dialog and reposition it in the best device    }
  427.     dt := DialogTHndl(GetResource ('DLOG', dialogID));
  428.     HNoPurge (Handle(dt));
  429.     CenterDialogBestDevice (dt);
  430.     
  431.     {    Show it    }
  432.     dp := GetNewDialog (dialogID, nil, WindowPtr(-1));
  433.     GetPort (oldPort);
  434.     SetPort (dp);
  435.     
  436.     {    Save our params handle in the window refcon for the user items    }
  437.     SetWRefCon(dp,LONGINT(theParams));
  438.     
  439.     {    Setup the user items and initialize all controls to initial state    }
  440.     SetupDItems(dp);
  441.     RecalcDItems(theParams,dp);
  442.     
  443.     DoFilter (src, params^.destImage, 100, 100, theParams^^.filterIntensity);
  444.     
  445.     REPEAT
  446.         ModalDialog (nil, item);
  447.         CASE item OF
  448.             forwardItem:
  449.                 BEGIN
  450.                     theParams^^.backward := false;
  451.                     RecalcDItems(theParams,dp);
  452.                 END;
  453.             backwardItem:
  454.                 BEGIN
  455.                     theParams^^.backward := true;
  456.                     RecalcDItems(theParams,dp);
  457.                 END;
  458.         END;
  459.     UNTIL ((item = ok) OR (item = cancel));
  460.     
  461.     SetPort (oldPort);
  462.     DisposDialog (dp);
  463.     HPurge (Handle(dt));
  464.     
  465.     {    Restore pixel maps state    }
  466.     HSetState (Handle(src),stateSrc);
  467.     SetPixelsState(src,pmStateSrc);
  468.     HSetState (Handle(dest),stateDest);
  469.     SetPixelsState(dest,pmStateDest);
  470.  
  471.     IF (item = cancel) THEN BEGIN
  472.         DisposHandle(Handle(theParams));
  473.         params^.parameters := nil;
  474.         DoParameters := FiltErrReported;
  475.         Exit (DoParameters);
  476.     END;
  477.     params^.parameters := Handle(theParams);
  478.     DoParameters := noErr;
  479. END;
  480.  
  481. PROCEDURE DoFilter (src: PixMapHandle; destGW: GWorldPtr; current,total,
  482.     intensity: INTEGER);
  483. {    Perform the filtering operation    }
  484. VAR
  485.     r,newRect:        Rect;
  486.     oldFore,oldBack:    RGBColor;
  487.     oldGW:    GWorldPtr;
  488.     dest:    PixMapHandle;
  489.     oldGD:    GDHandle;
  490. BEGIN
  491.     r := src^^.bounds;
  492.     
  493.     {    Check for pre-System 7 QD bug (GetGWorldPixMap didn't work)    }
  494.     IF (IntegerPtr(SysVersion)^ < $700) THEN
  495.         dest := destGW^.portPixMap
  496.     ELSE
  497.         dest := GetGWorldPixMap (destGW);
  498.     
  499.     {    Save state    }
  500.     
  501.     GetGWorld (oldGW,oldGD);
  502.     SetGWorld (destGW,GetGWorldDevice(destGW));
  503.     GetForeColor (oldFore);
  504.     GetBackColor (oldBack);
  505.     RGBForeColor (RGBColorPtr(RGBBlack)^);
  506.     RGBBackColor (RGBColorPtr(RGBWhite)^);
  507.     
  508.     { perform filter }
  509.     
  510.     CopyBits(BitMapHandle(src)^^,BitMapHandle(dest)^^,r,r,srcCopy,nil);
  511.     
  512.     {    Wait for accelerators    }
  513.     WHILE (NOT (QDDone(GrafPtr(destGW)))) DO;
  514.     
  515.     {    Restore state    }
  516.     
  517.     RGBForeColor (oldFore);
  518.     RGBBackColor (oldBack);
  519.     SetGWorld(oldGW,oldGD);
  520. END;
  521.  
  522. FUNCTION DoProcessFrame (params: FiltParamPtr; currentStep, totalSteps:
  523.     INTEGER) : FiltErr;
  524.  
  525. VAR
  526.     pmStateSrc,pmStateDest:    GWorldFlags;
  527.     destGW:        GWorldPtr;
  528.     src,dest:    PixMapHandle;
  529.     stateSrc,stateDest:    SignedByte;
  530.     p:    ParamsHandle;
  531.     dummy:    BOOLEAN;
  532. BEGIN
  533.     p := ParamsHandle(params^.parameters);
  534.     
  535.     IF (TestAbort (params)) THEN BEGIN
  536.         DoProcessFrame := FiltErrReported;
  537.         Exit (DoProcessFrame);
  538.     END;
  539.     
  540.     src := params^.srcImage;
  541.     destGW := params^.destImage;
  542.     
  543.     {    Check for pre-System 7 QD bug (GetGWorldPixMap didn't work)    }
  544.     IF (IntegerPtr(SysVersion)^ < $700) THEN
  545.         dest := destGW^.portPixMap
  546.     ELSE
  547.         dest := GetGWorldPixMap (destGW);
  548.     
  549.     {    Spin the beachball    }
  550.     UpdateProgress(params,0,2);
  551.     
  552.     { save offscreen pixels state and lock them down for drawing }
  553.     pmStateSrc := GetPixelsState(src);
  554.     dummy := LockPixels (src);
  555.     stateSrc := HGetState (Handle(src));
  556.     MoveHHi (Handle(src));
  557.     HLock (Handle(src));
  558.     
  559.     pmStateDest := GetPixelsState(dest);
  560.     dummy := LockPixels (dest);
  561.     stateDest := HGetState (Handle(dest));
  562.     MoveHHi (Handle(dest));
  563.     HLock (Handle(dest));
  564.         
  565.     { perform filter    }
  566.     IF (p^^.backward) THEN
  567.         DoFilter (src,destGW,totalSteps-currentStep,totalSteps,p^^.filterIntensity)
  568.     ELSE
  569.         DoFilter (src,destGW,currentStep,totalSteps,p^^.filterIntensity);
  570.     
  571.     UpdateProgress (params,2,2);
  572.  
  573.     HSetState (Handle(src),stateSrc);
  574.     SetPixelsState(src,pmStateSrc);
  575.     HSetState (Handle(dest),stateDest);
  576.     SetPixelsState(dest,pmStateDest);
  577.     
  578.     DoProcessFrame := noErr;
  579. END;
  580.  
  581. END.
  582.